; INTEL MDS FDC CARD EMULATOR
;
; SOURCED NOV 79 BY TREVOR MARSHALL
;		    Dept E & E Eng
;		    University of West Australia
;		    NEDLANDS, W.A. 6009
;
; This version of the source produces, after linking,
; object code in memory from 600H to A000H to enable
; a Prom Programmer resident in the host system
; to directly program the object file
;
; A version ORGed at F800H is compiled seperately
; and is provided on the master disk as MDSABS.Z80
;
;
; REGISTER USAGE DURING READ AND WRITE:
; D contains the (auto wait) drive control byte
; IX points to the IOPB base
; HL points to the current memory DMA address
; B contains the input byte counter for INI & OUTI
; C contains DDATA for INI & OUTI
; B' (alternate) contains the number of records required
; D' (alternate) contains the current sector address
; E' (alternate) contains the retry count
; H' (alternate) contains the previously logged track
; AF' (alternate) contains the previous drive mask
; to skip read trk# if it is already logged
; and allow for fast response without interleaving
;
; FDC CONTROLLER DEFINITIONS:
DCONTROL:EQU	63H	;Control Port
DFLAGS:	 EQU	63H	;FDC Status Port
DDATA:   EQU	67H	;1793 Data Port
DSTATUS: EQU	64H	;1793 Status Port
DCOMMAND:EQU	64H	;1793 Command Port
DTRACK:	 EQU	65H	;1793 Track Port
DSECTOR: EQU	66H	;1793 Sector Port
DSIDE:	EQU	4	;Bit 1=0 selects side 1
			; of D/S drives
;
; DISK CONTROL PORT HARDWARE ENVIRONMENT
; Bits 0-3 select drives 1-4
; Bit 7 enables auto wait on accesses to DDATA
; Bit 5 enables dsk drive motors 
; Bit 4 selects single density (S/D)
;
; DISK STATUS PORT HARDWARE REQUIREMENTS
; Bit 0 is 1793 INTREQ status output
; Bit 5 indicates head is loaded
;
; DISK DENSITY DEFINITIONS
MAXSECTORS: EQU 1AH  ;Single Density
MAXTRACKS:   EQU 4CH  ; 8" Disks
;
; PIO DEFINITIONS:
ACOMMAND:EQU	7AH
ADATA:   EQU	78H
BCOMMAND:EQU	7BH
BDATA:   EQU	79H
;
; BUS CONTROL:
RELEASE: EQU	7FH	;I/O To this addr releases bus
HOLD:    EQU	7CH	;I/O to this addr requests bus
;
; MDS PORT DEFINITIONS:
STATUS:  EQU	7EH	;S100 Bus reads 78H
;Note that port 78H is referenced directly in ASERVICE
;TYPE:	Not Implemented
RESULT:  EQU	7DH	;S100 Bus reads 7BH
;RESET: Not Implemented
IOPBLOW: EQU	79H
IOPBHIGH:EQU	7AH
;
; DISKETTE INSTRUCTION CODES:
NOP:     EQU	0	
SEEK:	 EQU	1
FORMAT:	 EQU	2
RECALIB: EQU 	3
READ:	 EQU	4
VERIFY:	 EQU	5
WRITE:	 EQU	6
WRITEDEL:EQU	7	;Write deleted data mark;
;
; AS THERE IS NO STACK RAM AVAILABLE ALL CODE
; MUST BE SEQUENTIAL
;
; THE PROM PROGRAMMER REQUIRES AN ADDRESS OFFSET
; OF 0F200H FOR THIS ORG 0F800H CODE
;
; THE FOLLOWING MACROS FORM COMMONLY USED ROUTINES
;
STOP:	MACRO
	LD	SP,STACK; Point at ROM stack
	IN	A,RELEASE
	RETI
	MEND
DONE: MACRO   ;Set up status port & stop
	LD	A,0FH
	OUT	STATUS,A
	STOP
	MEND
;
INTREQ:	MACRO	;Wait for intreq
LL#SYM:	IN	A,DFLAGS ;Loop until 1793 INTREQ active
	RRA
	JR	NC,LL#SYM
	MEND
;
ERRORS: MACRO	#LOOP,#MASK ;Check 1793 for errors
	INTREQ
	IN	A,DSTATUS
	EXX	;Save working registers
	LD	C,A	;Save error byte
	AND	80H	;Not ready mask
	EXX
	JR	NZ,#LOOP	;Loop until drive ready
	EXX
	LD	A,C
	AND	#MASK	;General error mask
	JP	NZ,SELECT+OFFSET ;Retry if any errors
	EXX
	MEND
;
SETUP:	MACRO	;Set up read and write common parameters
LP:	DL	AA#SYM
AA#SYM:	INC	D	;Sector #
	LD	A,D
	OUT	DSECTOR,A
	EXX
	LD	A,D
	OUT	DCONTROL,A
	LD	B,80H	;Input byte count
	IN	A,DFLAGS ;Is head loaded?
	AND	20H
	JR	Z,J2#SYM ;No
	LD	A,0
	JR	J3#SYM
J2#SYM: LD	A,04H    ;Head load mask for 1793
J3#SYM	DL	$
	MEND
;
CHECK:	MACRO	;See if read or write is complete
	EXX
	DJNZ	LP
	EXX
	LD	B,0	;Done
	JP	FINISH+OFFSET
	MEND
;
; NOTE THAT THE 1793 NEEDS TIME TO CALCULATE CRC
; BEFORE THE ERROR STATUS IS AVAILABLE
; IF THE DRQ IS NOT SERVICED
; The following delay loop is sufficient (4MHz Z-80)
DELAY:	MACRO
	LD	B,30H	;12 to 15 give CRC errors
; 0 to 12 give BUSY flag
	DJNZ	$
	MEND
;
; Check for 1793 errors during read and write
DERROR: MACRO	#MASK
	INTREQ
	IN	A,DSTATUS
	EXX
	LD	C,A	;Save error byte
	EXX
; Note that we will not check for deleted data marks
	AND	#MASK
	JP	NZ,S2+OFFSET ; Retry errors
	MEND

; LINKER AND PROM PROGRAMMER CODE REQUIREMENTS
	ORG	100H
	JP	0
OFFSET: EQU	0F800H-600H
; Setup vectors at top of ROM
	ORG	9F4H	;Effectively FBF4
VEC1:	DW	ASERVICE+OFFSET
VEC2:	DW	BSERVICE+OFFSET
WAIT:	EI
	HALT	;wait for interrupt
; STACK: To enable interrupt structure we need a 'stack'
	DW	WAIT+OFFSET
STACK:	EQU	$+OFFSET
	DW	WAIT+OFFSET 
	DW	WAIT+OFFSET
;
; Hardware Reset Code
	ORG	600H
START:	JP	0F804H
	NOP
; The ORG address is now effectively F804H
; Now setup PIO & Interupts
;
	IM2
	LD	A,0FBH
	LD	I,A	;Int vector table near FBF2
	LD	A,0F4H	;IVEC(A) = FBF4
	OUT	ACOMMAND,A
	LD	A,0F6H	;IVEC(B) = FBF6
	OUT	BCOMMAND,A
	LD	A,4FH	;MODE 1
	OUT	ACOMMAND,A
	OUT	BCOMMAND,A
	IN	A,ADATA	;Set READY handshake
	IN	A,BDATA
	LD	A,87H	;Enable PIO interrupt mode
	OUT	ACOMMAND,A
	OUT	BCOMMAND,A
; Now setup output latches
	LD	A,00
	OUT	RESULT,A
	LD	A,0BH	;:F0:,:F1: ready, not D/D
	OUT	STATUS,A
	EX	AF,AF'
	LD	A,0	;Clear drive select mask
	EX	AF,AF'
	EXX
	LD	H,0FFH	;Clear logged trk
	EXX
	LD	SP,STACK
	EI
	HALT
; Routine to service PIO inputs
; (79) go to IX lower, (7A) to IX upper
;
ASERVICE: DI
	OUT	HOLD,A	; Lock out bus until done
	NOP	;Allow time for bus control
	NOP
	IN	A,78H ; S100 Status port read (& reset)
;		      ; must be done during bus access
	IN	A,ADATA
	LD	IX,0
	LD	C,A
	LD	B,0
	ADD	IX,BC	;Now have A in IX
	STOP
BSERVICE: DI
	 OUT	HOLD,A	; Lock out bus until done
	IN	A,BDATA
	LD	B,A
	LD	C,0
	ADD	IX,BC	; IX now points to IOPB
; Now select side 0 of disks
	LD	A,2	;Set bit 1
	OUT	DSIDE,A
;
	EXX
	LD	E,0AH	;10 retries
	JR	SELECT
;
; NOW BEGIN DECODING COMMAND WORD
; 
; FIRST SELECT THE DRIVE (:F0:=A:, :F1:=C:)
;
S2:	EXX
	LD	H,0FFH	;Log off track if error occurs
;			;This will cause a RESTORE cmd
;			;To be executed for SEEK errors
SELECT:	LD	A,E
	CP	0	;Have we retried 10 times?
	LD	A,C	;Error byte

	JP	Z,EXIT+OFFSET
	DEC	E
	EXX
	LD	A,(IX+0)
	AND	3FH ;Check for acceptable channel word
	JP	NZ,CHERROR+OFFSET ; Channel error
;
;If the op is NOP,FORMAT or RECALIBRATE 
;Dont check address field
	LD	A,(IX+1)
	AND	07H	;Now have opcode field
	CP	1	;Seek
	JR	Z,DONTSKIP
	SUB	4
	JR	C,SKIP	; <=3
DONTSKIP:

; Now check for valid IOPB addresses
N12:	LD	A,(IX+3) ;Track address
	SUB	MAXTRACKS+1  ;max track # + 1
	JP	NC,ADDERROR+OFFSET
	LD	A,(IX+4)
;
; Must reset bits 4 & 5 of sector byte (a drive select bit)
; to retain compatibility with S/D MDS systems
	AND	A,09FH
	JP	Z,ADDERROR+OFFSET ;Zero sector #
	SUB	MAXSECTORS+1 ;Max sector # + 1
	JP	NC,ADDERROR+OFFSET
	LD	A,(IX+4) ;Sector addr
;
; Must reset bit 5 of sector byte (a drive select bit)
; to retain compatibility with S/D MDS systems
	AND	A,09FH
	LD	C,(IX+2) ;Number of records requested
	ADD	A,C
	SUB	A,MAXSECTORS+2
	JP	NC,ADDERROR+OFFSET ;Final sector >1AH
; Address errors trapped, now seek to required track
;
SKIP:	LD	A,(IX+1) ;Instruction word
	AND	30H	;Get drive mask
	RRC	A
	RRC	A
	RRC	A
	RRC	A
	CP	3 ; S/D MDS drive #1 mask
	JR	Z,BSELECT ;Select drive B
; Dont allow for more than 2 drives
	LD	D,0B1H	;Auto wait,motor on,S/D,A:
	JR	J3
BSELECT: LD	D,0B2H	;Auto wait,motor on,S/D,B:
; D contains the drive control byte with auto wait set
;
; Now check for recalibrate, nop & format instructions
;
J3:	LD	A,(IX+1)
	AND	07H
	CP	RECALIB	
	JR	Z,RESTORE
	CP	NOP
	JR	Z,DONOP
	CP	FORMAT
	JR	Z,DONOP
	JR	DOSEEK;Not one of these ops
DONOP:	LD	B,0
	JP	FINISH+OFFSET
RESTORE: LD	A,D	;Drive control byte
	AND	7FH	;Reset auto wait
	OUT	DCONTROL,A
	 LD	A,0DH	;1793 Restore command
	OUT	DCOMMAND,A
	ERRORS	RESTORE,99H
	LD	B,0
	JP	FINISH+OFFSET
;
DOSEEK: 	;Read, write, verify & seek
		;all require a seek op first
; Now check if drive is logged
; trk # will then be available without the read addr op
;
	EX	AF,AF'
	CP	A,D	;Is it the same as the last?
	LD	A,D
	JR	NZ,LOG	;No, log it
	EX	AF,AF'
	EXX
	LD	A,H	; fetch last trk #
	EXX
	LD	B,A	;Save it
	CP	(IX+3) ;Is the desired trk the same?
	JR	NZ,LOG2 ;No, read address
	JP	LOGGED+OFFSET
; Check current track # by reading address from disk
;NOTE THAT THE 1793 DOES NOT HEAD LOAD ON 'READ ADDRESS'
; IF THE READY INPUT IS NOT ACTIVE
; so we must issue a dummy SEEK command first
;
LOG:	EX	AF,AF'
; Will first execute a RESTORE command on the new drive
REST1:	LD	A,D
	AND	7FH	;Reset auto wait
	OUT	DCONTROL,A
	LD	A,0DH	;1793 Restore command
	OUT	DCOMMAND,A
	ERRORS	REST1,99H
;
; Now seek to desired track
LOG2:	LD	A,D
	AND	7FH	;Reset auto wait
	OUT	DCONTROL,A
	IN	A,DTRACK
	OUT	DDATA,A ;desired trk=current one
L3:	LD	A,1AH   ;Seek, no verify
	OUT	DCOMMAND,A
	ERRORS	LOG2,99H
;
L2:	LD	A,0C4H	;1793 Read address cmd
	OUT	DCOMMAND,A
  ;Discard the data bytes
	INTREQ
	DELAY
	IN	A,DSTATUS
	AND	19H	; status would not be valid
	JR	NZ,L2
	IN	A,DSECTOR   ;where the 1793 puts the trk
	LD	B,A	  ;Save it
	EXX
	LD	H,A	; log it
	EXX
;
; Now must clear the data request by reading DDATA
	IN	A,DDATA
;
LOGGED:	LD	A,B	;Fetch current track addr
	CP	(IX+3)  ;Is it the same?
	OUT	DTRACK,A
	JR	Z,J11	;Yes, skip read addr
	LD	A,(IX+3)
	OUT	DDATA,A
S1:	LD	A,D
	AND	7FH
	OUT	DCONTROL,A ;Disable auto wait
	LD	A,1DH	;Seek with verify
	OUT	DCOMMAND,A
	ERRORS	S1,99H
; Now have desired track, check to see if op was seek
;
J11:	LD	A,(IX+1)
	AND	07H
	CP	SEEK
	JP	Z,FINISH+OFFSET ; was seek only
;
; Now calculate the number of records
; and setup dedicated register values
;
	EXX
	LD	B,(IX+2)  ;Number of records
	LD	A,(IX+4)
;
; Must reset bit 5 of sector byte (a drive select bit)
; to retain compatibility with S/D MDS systems
	LD	D,09FH
	AND	A,D
	LD	D,A
	DEC	D	 ;Initial sector (-1 for SETUP)
	EXX
;
	LD	H,(IX+6) 
	LD	L,(IX+5)
	LD	C,DDATA
;
; Was it a read, write or writedel operation?
	LD	A,(IX+1)
	AND	07H
	CP	READ
	JR	Z,DOREAD
	CP	WRITE
	JP	Z,DOWRT+OFFSET
	CP	WRITEDEL
	JP	Z,DOWRTDEL+OFFSET
	CP	VERIFY
	JP	DOVERIFY+OFFSET
;
DOREAD: EXX
	SETUP
	ADD	A,88H	;1797 Read command
	OUT	DCOMMAND,A
J13:	IN	A,DFLAGS
	RRA
	JR	C,J12 ;INTREQ = error or read complete
	INI
	JP	NZ,J13+OFFSET ;Fetch next data byte
; Z set when B=0, ie 128 bytes have been read
J12:	DERROR	9DH
	CHECK
;
DOVERIFY: EXX	;Similar to DOREAD
	SETUP
	ADD	88H	;1797 compatible
	OUT	DCOMMAND,A
J14:	IN	A,DFLAGS
	RRA
	JR	C,J15
	IN A,(C)	;Discard input data
	JP	J14+OFFSET
J15:	DERROR	9DH
	CHECK
;
DOWRT:	EXX	;Similar to read in structure
	SETUP
	ADD	A,0A8H	;1797 compatible
	OUT	DCOMMAND,A
J16:	IN	A,DFLAGS
	RRA
	JR	C,J17
	OUTI
	JP	NZ,J16+OFFSET
J17:	DERROR 0FDH
	CHECK
;
DOWRTDEL: EXX
	SETUP
	ADD	A,0A9H	;1797 compatible
	OUT	DCOMMAND,A
J20:	IN	A,DFLAGS
	RRA
	JR	C,J19
	OUTI
	JP	NZ,J20+OFFSET
J19:	DERROR	0FDH
	CHECK
;
; Address and Channel error routines
;
;THIS ENTRY IS VALID DURING ERROR TRAPS
A1:	LD	A,08H
	OUT	RESULT,A
	JP	STAT+OFFSET
;
; 1793 Error exit routines
;
; Will print out error messages for ease of debugging
; Will use the RST7,RST6 area of RAM for stack
;
EXIT:	EXX
	LD	SP,0038H ; Out of ISIS area
	PUSH	AF
	CALL	PMSGFOLLOWING+OFFSET
	DB	0DH,'1793',0A0H
	POP	AF
	PUSH	AF
	CALL	P2HEX+OFFSET
	CALL	PRINTIOPB+OFFSET
	POP	AF
	LD	B,0	;Use B to hold result byte
	BIT	0,A
	JR	Z,J5
	SET	0,B
	SET	7,B
J5	BIT	1,A	;DRQ error
	JR	Z,J6
	SET	4,B
J6:	BIT	2,A	;Lost data
	JR	Z,J7
	SET	4,B
J7:	BIT	3,A	;CRC
	JR	Z,J8
	SET	1,B
J8:	BIT	4,A	;Seek error
	JR	Z,J9
	SET	2,B
J9:	BIT	6,A
	JR	Z,FINISH
	SET	5,B
FINISH:	LD	A,B
	OUT	RESULT,A
; The following code has been nulled as it
; causes the motors to 'cycle' in speed too much.
; A delayed turnoff is needed
; Switch off the drive motors
;	LD	A,D
;	AND	5FH	;motors + wait off
;	OUT	DCONTROL,A
STAT:	DONE
;
; OUTPUT MESSAGE ROUTINES
PRINTIOPB: CALL	PMSGFOLLOWING+OFFSET
	DB	' ERROR, IOPB A',0D4H
	PUSH	IX
	POP	HL
	CALL	SPACE+OFFSET
	CALL	PMSGFOLLOWING+OFFSET
	DB	', CONTENTS:',0A0H
	LD	A,(IX+0)
	CALL	P2HEX+OFFSET
	LD	A,(IX+1)
	CALL	P2HEX+OFFSET
	LD	A,(IX+2)
	CALL	P2HEX+OFFSET
	LD	A,(IX+3)
	CALL	P2HEX+OFFSET
	LD	A,(IX+4)
	CALL	P2HEX+OFFSET
	LD	L,(IX+6) ;Print in reverse order
	LD	H,(IX+5)
	CALL	SPACE+OFFSET
	CALL	PMSGFOLLOWING+OFFSET
	DB	0DH,8AH
	RET
ADDERROR: LD	SP,0038H ;Not in ISIS area
	CALL	PMSGFOLLOWING+OFFSET
	DB	0DH,'ADDRESS',0A0H
	CALL	PRINTIOPB+OFFSET
	JP	A1+OFFSET
CHERROR: LD	SP,0038H
	CALL	PMSGFOLLOWING+OFFSET
	DB	0DH,'CHANNEL',0A0H
	CALL	PRINTIOPB+OFFSET
	JP	A1+OFFSET
PMSG:	PUSH	AF
PS1:	LD	A,(HL)
	INC	HL
	CALL	PCHR+OFFSET
	RLA
	JR	NC,PS1
	POP	AF
	RET
PMSGFOLLOWING:
	EX	(SP),HL
	CALL	PMSG+OFFSET
	EX	(SP),HL
	RET
PCHR:	PUSH	AF
;SJ2:	IN	A,0F7H	;CRT STATUS
;	AND 	1
;	JR	Z,SJ2
;	POP	AF
;	PUSH	AF
;	OUT	0F6H,A	;CRT DATA
SJ1:	IN	A,0FBH	;PRINTER STATUS
	AND	1
	JR	Z,SJ1
	POP	AF
	OUT	0FBH,A	;PRINT DATA
	RET
SPACE:	LD	A,20H
	CALL	PCHR+OFFSET
	LD	A,H
	CALL	P2HEX+OFFSET
	LD	A,L
P2HEX: PUSH	AF
	LD	A,20H
	CALL	PCHR+OFFSET
	POP	AF
	 CALL	P1HEX+OFFSET
	RRA
P1HEX:	RRA
	RRA
	RRA
	RRA
	PUSH	AF
	AND	0FH
	CP	10
	JR	C,PH1
	ADD	7
PH1:	ADD	30H
	CALL	PCHR+OFFSET
	POP	AF
	RET
	END	START
